/**
 * Copyright (c) 2015-2030 by Beijing Yanlight Co., Ltd.
 * All rights reserved.
 */
package com.yanlight.util;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 
 * @author haolw
 * @time 2017/07/18
 */
public class XmlUtil {

	/**
	 * 转换 map to xml
	 * 
	 * @param map
	 * @return
	 */
	public static String mapToXml(Map<String, String> map) {
		StringBuilder xml = new StringBuilder(16);
		xml.append("<xml>");
		for (Map.Entry<String, String> entry : map.entrySet()) {
			xml.append(createElement(entry.getKey(), entry.getValue()));
		}
		xml.append("</xml>");
		return xml.toString();
	}

	/**
	 * 转换 map to xml
	 * 
	 * @param map
	 * @return
	 */
	public static String mapToXmlWithCDATA(Map<String, String> map) {
		StringBuilder xml = new StringBuilder(16);
		xml.append("<xml>");
		for (Map.Entry<String, String> entry : map.entrySet()) {
			xml.append(createCDATAElement(entry.getKey(), entry.getValue()));
		}
		xml.append("</xml>");
		return xml.toString();
	}

	/**
	 * 转换 map为xml格式数据，非数字用<key></>![CDATA[value]]</key>
	 * 
	 * @param arr
	 * @return
	 */
	public static String mapToXmlWithParse(Map<String, String> arr) {
		StringBuilder xml = new StringBuilder(16);
		xml.append("<xml>");
		for (Map.Entry<String, String> entry : arr.entrySet()) {
			String key = entry.getKey();
			String val = entry.getValue();
			if (isNumeric(val))
				xml.append(createElement(key, val));
			else {
				xml.append(createCDATAElement(key, val));
			}
		}
		xml.append("</xml>");
		return xml.toString();
	}

	/**
	 * xml 转换为 字典序 map
	 *
	 * @param xml
	 * 
	 * @return
	 */
	public static SortedMap<String, String> xmlToSortedMap(String xml) {
		if (StringUtils.isBlank(xml)) {
			return null;
		}
		SortedMap<String, String> map = new TreeMap<String, String>();
		toMap(xml, map);
		return map;
	}

	/**
	 * xml 转换为 字典序 map
	 *
	 * @param xml
	 * 
	 * @return
	 */
	public static Map<String, String> xmlToMap(String xml) {
		if (StringUtils.isBlank(xml)) {
			return null;
		}
		Map<String, String> map = new HashMap<String, String>();
		toMap(xml, map);
		return map;
	}

	private static void toMap(String xml, Map<String, String> map) {
		try {
			Document doc = DocumentUtil.parseText(xml);
			Element root = doc.getRootElement();
			for (Iterator<?> iterator = root.elementIterator(); iterator.hasNext();) {
				Element e = (Element) iterator.next();
				map.put(e.getName(), e.getText());
			}
		} catch (DocumentException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 对象转为xml字符串
	 * 
	 * @param o
	 * @return
	 */
	public static String objectToXml(Object o) {
		return mapToXml(objectToSortedMap(o));
	}

	/**
	 * 对象转为xml字符串
	 * 
	 * @param o
	 * @return
	 */
	public static String objectToXmlWithCDATA(Object o) {
		return mapToXmlWithCDATA(objectToSortedMap(o));
	}

	/**
	 * xml 转 object
	 * 
	 * @param xml
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T xmlToObject(String xml, Class<T> clazz) {
		SortedMap<String, String> map = xmlToSortedMap(xml);
		BeanWrapper wrapper = new BeanWrapperImpl(clazz);
		PropertyValues pvs = new MutablePropertyValues(map);
		wrapper.setPropertyValues(pvs, true);
		return (T) wrapper.getWrappedInstance();
	}

	/**
	 * 从对象转换为字典序的map
	 *
	 * @param o
	 * 
	 * @return
	 */
	private static SortedMap<String, String> objectToSortedMap(Object o) {
		SortedMap<String, String> map = new TreeMap<String, String>();
		Field[] fields = o.getClass().getDeclaredFields();
		for (Field f : fields) {
			f.setAccessible(true);
			try {
				if (f.get(o) != null && f.get(o) != "") {
					map.put(f.getName(), f.get(o).toString());
				}
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}
		return map;
	}

	/**
	 * 判断字符串是不是纯数字组成的
	 *
	 * @param str
	 * 
	 * @return
	 */
	private static boolean isNumeric(String str) {
		return str != null && str.matches("\\d *");
	}

	/**
	 * 创建普通 xml element
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	private static String createElement(String key, String value) {
		return "<" + key + ">" + value + "</" + key + ">";
	}

	/**
	 * 创建 CDATA xml element
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	private static String createCDATAElement(String key, String value) {
		return "<" + key + "><![CDATA[" + value + "]]></" + key + ">";
	}

}
